#include "high_accuracy.h"
#include <iostream>

BigInteger operator"" _big(const char* str, size_t size) {
    return BigInteger(str);
}

BigInteger::BigInteger(long long num) {
    this->capacity = 4;
    this->data = std::unique_ptr<unsigned[]>(new unsigned[4]());
    this->positive = num >= 0;
    if (num == LLONG_MIN) {
        this->data[2] = 1;
        this->size = 3;
    } else {
        if (!this->positive) num = -num;
        this->data[0] = num & -1;
        num >>= INT_LENGTH;
        this->data[1] = num & -1;
        this->size = num ? 2 : 1;
    }
}

BigInteger::BigInteger(): BigInteger(0LL) {}

BigInteger::BigInteger(const char *str) {
    int base = 10;
    int len;
    this->positive = true;
    if (str[0] == '-') {
        this->positive = false;
        str++;
    }
    for (len = 0; str[len] != '\0'; len++) {
        if (str[len] < '0' || str[len] > '9') {
            if (len == 1 && (str[1] == 'x' || str[1] == 'X') && str[0] == '0') {
                base = 16;
            } else if (!(base == 16 && ((str[len] >= 'a' && str[len] <= 'f') || (str[len] >= 'A' && str[len] <= 'F')))) {
                len = 0;
                break;
            }
        }
    }
    if (len == 0) {
        printf("wrong format number\n");
        this->capacity = 4;
        this->data = std::unique_ptr<unsigned[]>(new unsigned[4]());
        this->positive = true;
        this->data[0] = 0;
        this->size = 0;
        return;
    }
    if (base != 16 && str[0] == 0) base = 8;
    int first_pos = 0, last_pos = len - 1;
    switch (base) {
        case 16: first_pos = 2; break;
        case 8: first_pos = 1; break;
        default: break;
    }
    this->capacity = 9 * len;
    this->size = 0;
    this->data = std::unique_ptr<unsigned[]>(new unsigned[this->capacity]());
    long long tmp = 0;
    int times = 0;
    while (last_pos >= first_pos) {
        long long current_num;
        if (str[last_pos] >= '0' && str[last_pos] <= '9') {
            current_num = str[last_pos] - '0';
        } else if (str[last_pos] >= 'a' && str[last_pos] <= 'f') {
            current_num = str[last_pos] - 'a' + 10;
        } else {    // A - F
            current_num = str[last_pos] - 'A' + 10;
        }
        switch (base) {
            case 16: current_num <<= (4 * times); break;
            case 8: current_num <<= (3 * times); break;
            default: {
                for (int i = 0; i < times; i++) {
                    current_num *= 10;
                }
                break;
            }
        }
        tmp += current_num;
        if (tmp >= UNSIGNED_MAX) {
            this->data[this->size] = UNSIGNED_MAX & tmp;
            this->size++;
            tmp >>= sizeof(int) * 8;
            times = 1;
        } else {
            times++;
        }
        last_pos--;
    }
    if (tmp > 0) {
        this->data[this->size] = UNSIGNED_MAX & tmp;
        this->size++;
    } else if (this->size != 0) {
        this->positive = false;
    }
}

BigInteger::BigInteger (std::string str): BigInteger(str.c_str()) {}

BigInteger::BigInteger(const BigInteger& another) {
    this->capacity = another.capacity;
    this->positive = another.positive;
    this->size = another.size;
    this->data = std::unique_ptr<unsigned[]>(new unsigned[this->capacity]());
    for (int i = 0; i < this->size; i++) this->data[i] = another.data[i];
}

BigInteger::BigInteger(BigInteger &&another) noexcept
    : positive(another.positive), size(another.size), capacity(another.capacity), data(another.data.release()) {}

std::string BigInteger::hex_str() {
    if (this->size == 0) return std::string("0");
    std::ostringstream o_stream;
    if (!this->positive) {
        o_stream << "-";
    }
    o_stream << "0x";
    for (int i = this->size - 1; i >= 0; i--) {
        o_stream << std::hex << this->data[i];
    }
    return o_stream.str();
}

void BigInteger::setCapacity(int new_capacity) {
    if (new_capacity <= this->capacity) return;
    auto new_data = new unsigned[new_capacity]();
    for (int i = 0; i < this->size; i++) new_data[i] = this->data[i];
    this->data.reset(new_data);
}

bool operator==(const BigInteger &lhs, const BigInteger &rhs) {
    bool result = lhs.positive == rhs.positive && lhs.size == rhs.size;
    if (!result) return false;
    for (int i = 0; i < lhs.size; i++)
        if (lhs.data[i] != rhs.data[i])
            return false;
    return true;
}

bool operator<(const BigInteger &lhs, const BigInteger &rhs) {
    if (lhs.positive && !rhs.positive) return false;
    if (!lhs.positive && rhs.positive) return true;
    bool compare_abs = true;
    if (lhs.size > rhs.size) {
        compare_abs = false;
    } else if (lhs.size < rhs.size) {
        compare_abs = true;
    } else {
        for (int i = lhs.size - 1; i >= 0; i--) {
            if (lhs.data[i] > rhs.data[i]) {
                compare_abs = false;
                break;
            }
        }
    }
    return compare_abs == lhs.positive;
}

bool operator>(const BigInteger &lhs, const BigInteger &rhs) {
    return rhs < lhs;
}

// BigInteger operator+(const BigInteger &lhs, const BigInteger &rhs) {
//     int max_capacity = lhs.capacity > rhs.capacity ? lhs.capacity : rhs.capacity;
//     int max_size = lhs.size > rhs.size ? lhs.size : rhs.size;
//     BigInteger result;
//     result.capacity = max_capacity > max_size + 2 ? max_capacity : max_capacity + 2;

// }